﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using Styx.Common;
using Styx.WoWInternals.WoWObjects;
using Styx;
using Styx.WoWInternals;
using System.Diagnostics;
using Styx.CommonBot;
using Styx.TreeSharp;
using Action = Styx.TreeSharp.Action;

namespace XS
{
    public class XSCommon
    {
        /*
         * 30-11-2012
         * 
         * i like the way CLU handles their state, i'll adapt this.
         * 
         *   = well .. nothing!
         * P = partially coded, partially tested
         * U = untested but coding done
         * T = tested in envoirement (fully)
         * 
         * 
         * 
         *  HUNTER                      MAGE                    DRUID
         *      [ ] NoSpec                  [ ] NoSpec              [ ] NoSpec
         *      [P] Survival                [ ] Fire                [ ] Balance
         *      [P] Marksmanship            [ ] Frost               [ ] Restoration
         *      [P] BeastMastery            [ ] Arcane              [ ] Guardian
         *                                                          [ ] Feral
         *      
         *  PALADIN                     WARRIOR                 MONK
         *      [ ] Holy                    [ ] Fury                [ ] Windwalker
         *      [ ] Retribution             [ ] Arms                [ ] Mistwalker
         *      [ ] Protection              [ ] Protection          [ ] Brewmaster
         *      
         *
         *  PRIEST                      DEATHKNIGHT             SHAMAN
         *      [ ] Shadow                  [ ] Frost               [ ] Elemental
         *      [ ] Discipline              [ ] Unholy              [ ] Restoration
         *      [ ] Holy                    [ ] Blood               [ ] Enhancement
         *      
         *  WARLOCK                     ROGUE
         *      [ ] Affliction              [ ] dunno
         *      [ ] Destruction             [ ] dunno lol
         *      [ ] Demonology              [ ] i really dunno the specs
         */


        public static bool varDebug = false;


        public static void write(string msg, params object[] args)
        {
            if (msg != null)
            {
                Logging.Write(Colors.Crimson, msg, args);
            }
        }
        public static void writeDebug(string msg, params object[] args)
        {
            if (msg != null && varDebug)
            {
                Logging.Write(Colors.White, "[XS-DEBUG] " + msg, args);
            }
        }
        

        public static Version version = new Version(0,0,0,1);
        public static LocalPlayer Me { get { return StyxWoW.Me; } }
        public static bool WantButton
        {
            get
            {
                return false;
            }
        }
        public static void OnButtonPress()
        {
            write("No Button allowed, you have do pay me 1000€ first");
        }
        public static void Initialize(string Name, LocalPlayer Me)
        {
            write("-- stuff --");
        }
        public static void Pulse()
        {
            if (!StyxWoW.IsInGame || !StyxWoW.IsInWorld)
            {
                writeDebug("Loadscreen");
            }
        }
        public delegate WoWUnit UnitSelection(object unit);


        public static bool TargetIsBoss()
        {
            if (StyxWoW.Me.CurrentTarget.Name.Contains("Dummy") ||                                                                      //Boss= Dummy
                StyxWoW.Me.CurrentTarget.CreatureRank == WoWUnitClassificationType.WorldBoss ||                                         //or= Boss
                StyxWoW.Me.CurrentTarget.MaxHealth >= 37777777                                                                           //or more than 37.777.777 hp
                )
                return true;

            return false;
        }
        public static bool TargetIsPlayer()
        {
            if (Me.CurrentTarget.IsPlayer)
                return true;

            return false;
        }
        public static bool TargetIsValid()
        {
            if (StyxWoW.Me.GotTarget && StyxWoW.Me.CurrentTarget.IsAlive && StyxWoW.Me.CurrentTarget.Attackable && StyxWoW.Me.CurrentTarget.CanSelect && !StyxWoW.Me.CurrentTarget.IsFriendly)
                return true;

            return false;
        }


        public static int AddCount()
        {
            int count = 0;
            foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(true, true))
            {
                if (StyxWoW.Me.GotTarget
                    && u.IsAlive
                    && u.Combat /* maybe buggy? test */
                    && u.Guid != StyxWoW.Me.Guid
                    && !u.IsFriendly
                    && u.IsHostile
                    && u.Attackable
                    && !u.IsTotem
                    && !u.IsCritter
                    && !u.IsNonCombatPet
                    && u.GotTarget
                    && (u.Location.Distance(StyxWoW.Me.CurrentTarget.Location) <= 10 || u.Location.Distance2D(StyxWoW.Me.CurrentTarget.Location) <= 10))
                {
                    count++;
                }
            }
            return count;
        }   //returns number of adds within 10yd of the target who are in combat
        public static bool AddCluster()
        {
            int count = 0;
            foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(true, true))
            {
                if (StyxWoW.Me.GotTarget
                    && u.IsAlive
                    && u.Combat /* maybe buggy? test */
                    && u.Guid != StyxWoW.Me.Guid
                    && !u.IsFriendly
                    && u.IsHostile
                    && u.Attackable
                    && !u.IsTotem
                    && !u.IsCritter
                    && !u.IsNonCombatPet
                    && u.GotTarget
                    && (u.Location.Distance(StyxWoW.Me.CurrentTarget.Location) <= 5 || u.Location.Distance2D(StyxWoW.Me.CurrentTarget.Location) <= 5))
                {
                    count++;
                }
            }
            if(count >= 2)
                return true;

            return false;
        }   //returns true if >= 2 mobs are clustered, e.g. less than 5yd's away


        public static int MyAuraTimeMilliseconds(String SpellName, WoWUnit unit)
        {
            {
                if (unit.HasAura(SpellName))
                {
                    var auras = unit.GetAllAuras();
                    foreach (var a in auras)
                    {
                        if (a.Name == SpellName && a.CreatorGuid == Me.Guid)
                        {
                            return a.TimeLeft.Milliseconds;
                        }
                    }
                }
            }
            return 0;
        }
        public static int MyAuraTime(String SpellName, WoWUnit unit)    
        {
            {
                if (unit.HasAura(SpellName))
                {
                    var auras = unit.GetAllAuras();
                    foreach (var a in auras)
                    {
                        if (a.Name == SpellName && a.CreatorGuid == Me.Guid)
                        {
                            return a.TimeLeft.Seconds;
                        }
                    }
                }
            }
            return 0;
        }
        public static int AuraTime(String SpellName, WoWUnit unit)      //Used for checking debuff timers
        {
            {
                if (unit.HasAura(SpellName))
                {
                    var auras = unit.GetAllAuras();
                    foreach (var a in auras)
                    {
                        if (a.Name == SpellName)
                        {
                            return a.TimeLeft.Seconds;
                        }
                    }
                }
            }
            return 0;
        }
        public static bool IsMyAuraActive(String AuraName)
        {
            return Me.ActiveAuras.ContainsKey(AuraName) ? true : false;
        }
        public static uint IsMyAuraActiveStacks(String AuraName)
        {
            if (Me.ActiveAuras.ContainsKey(AuraName))
                return Me.Auras[AuraName].StackCount;

            return 0;
        }


        public static string safeName(WoWUnit unit)
        {
            if (unit != null)
            {
                return (unit.Name == StyxWoW.Me.Name) ? "Myself" : unit.Name;
            }
            return "No Target";
        }


        #region TimeToDeath by HandNavi
        /* Time to death code was made by HandNavi for the Junglebook CC, all credit goes to him! */
        private static uint _firstLife;
        private static uint _firstLifeMax;
        private static Stopwatch deathTimer = new Stopwatch();
        private static uint _currentLife;
        private static ulong _guid;
        public static long CalculateTimeToDeath(WoWUnit target)
        {
            if (target.Name.Contains("Training Dummy"))
            {
                return 111;
            }
            if (target.CurrentHealth == 0 || target.IsDead || !target.IsValid || !target.IsAlive)
            {
                return 0;
            }
            //Fill variables on new target or on target switch, this will lose all calculations from last target
            if (_guid != target.Guid || (_guid == target.Guid && target.CurrentHealth == _firstLifeMax))
            {
                _guid = target.Guid;
                _firstLife = target.CurrentHealth;
                _firstLifeMax = target.MaxHealth;
                deathTimer.Restart();
            }
            _currentLife = target.CurrentHealth;
            int timeDiff = deathTimer.Elapsed.Seconds;
            uint hpDiff = _firstLife - _currentLife;
            if (hpDiff > 0)
            {
                long fullTime = timeDiff * _firstLifeMax / hpDiff;
                long pastFirstTime = (_firstLifeMax - _firstLife) * timeDiff / hpDiff;
                long timeToDie = fullTime - pastFirstTime - timeDiff;

                if (timeToDie < 1) timeToDie = 1;
                return timeToDie;
            }
            if (hpDiff <= 0)
            {
                // Target was healed, reset to initial values
                _guid = target.Guid;
                _firstLife = target.CurrentHealth;
                _firstLifeMax = target.MaxHealth;
                deathTimer.Restart();
                return -1;
            }
            // Target is at full health
            if (_currentLife == _firstLifeMax)
            {
                return -1;
            }
            // No damage done, nothing to calculate
            return -1;
        }
        #endregion


        public static bool CanCast(string spell)
        {
            return CanCast(spell, StyxWoW.Me.CurrentTarget, 40);
        }
        public static bool CanCast(string spell, int distance)
        {
            return CanCast(spell, StyxWoW.Me.CurrentTarget, distance);
        }
        public static bool CanCast(string spell, WoWUnit target)
        {
            return CanCast(spell, target, 40);
        }
        public static bool CanCast(string spell, WoWUnit target, int distance)
        {
            switch(Me.Class)
            {
                case WoWClass.Hunter:
                    return (
                            SpellManager.HasSpell(spell) &&
                            SpellManager.Spells[spell].CooldownTimeLeft.TotalMilliseconds < 200 &&
                            SpellManager.Spells[spell].PowerCost < Me.CurrentFocus &&
                            SpellManager.Spells[spell].MaxRange >= target.Distance &&
                            target.InLineOfSpellSight &&
                            !Me.IsChanneling &&
                            (!Me.IsCasting || Me.CurrentCastTimeLeft.TotalMilliseconds < 200 || SpellManager.GlobalCooldownLeft.Milliseconds > 200)
                           );
                default:
                    return (
                            SpellManager.HasSpell(spell) &&
                            SpellManager.Spells[spell].CooldownTimeLeft.TotalMilliseconds < 200 &&
                            SpellManager.Spells[spell].PowerCost < Me.CurrentMana &&
                            SpellManager.Spells[spell].MaxRange >= target.Distance &&
                            target.InLineOfSpellSight &&
                            !Me.IsChanneling &&
                            (!Me.IsCasting || Me.CurrentCastTimeLeft.TotalMilliseconds < 200 || SpellManager.GlobalCooldownLeft.Milliseconds > 200)
                           );
            }
        }
        public static Composite CastSelfSpell(string spell, CanRunDecoratorDelegate cond)
        {
            return CastSpell(spell, ret => StyxWoW.Me, cond, "");
        }
        public static Composite CastSelfSpell(string spell, CanRunDecoratorDelegate cond, string context)
        {
            return CastSpell(spell, ret => StyxWoW.Me, cond, context);
        }
        public static Composite CastSpell(string spell, CanRunDecoratorDelegate cond)
        {
            return CastSpell(spell, ret => StyxWoW.Me.CurrentTarget, cond, "");
        }
        public static Composite CastSpell(string spell, CanRunDecoratorDelegate cond, string context)
        {
            return CastSpell(spell, ret => StyxWoW.Me.CurrentTarget, cond, context);
        }
        public static Composite CastSpell(string spell, UnitSelection onUnit, CanRunDecoratorDelegate cond)
        {
            return CastSpell(spell, onUnit, cond, "");
        }
        public static Composite CastSpell(string spell, UnitSelection onUnit, CanRunDecoratorDelegate cond, string context)
        {
            return (
                new Decorator(delegate(object a)
                {
                    if (!cond(a))
                        return false;
                    if (!CanCast(spell, onUnit(a)))
                        return false;
                    return onUnit(a) != null;
                },
                    new Sequence(
                        new Action(a => write("{2}[Spell] {0} -> {1} ({3}%)", spell, safeName(onUnit(a)), context, Math.Round(onUnit(a).HealthPercent, 1))),
                        new Action(a => SpellManager.Cast(spell, onUnit(a)))
                        )
                )
            );
        }
        public static Composite CastOnLocation(string spell, CanRunDecoratorDelegate cond)
        {
            return CastOnLocation(spell, ret => Me.CurrentTarget, cond, "");
        }
        public static Composite CastOnLocation(string spell, CanRunDecoratorDelegate cond, string context)
        {
            return CastOnLocation(spell, ret => Me.CurrentTarget, cond, context);
        }
        public static Composite CastOnUnitLocation(string spell, WoWUnit unit, CanRunDecoratorDelegate cond)
        {
            return CastOnLocation(spell, ret => unit, cond, "");
        }
        public static Composite CastOnUnitLocation(string spell, WoWUnit unit, CanRunDecoratorDelegate cond, string context)
        {
            return CastOnLocation(spell, ret => unit, cond, context);
        }
        public static Composite CastOnLocation(string spell, UnitSelection onUnit, CanRunDecoratorDelegate cond)
        {
            return CastOnLocation(spell, onUnit, cond, "");
        }
        public static Composite CastOnLocation(string spell, UnitSelection onUnit, CanRunDecoratorDelegate cond, string context)
        {
            return (
                new Decorator(delegate(object a)
                    {
                        if (!cond(a))
                            return false;
                        return onUnit != null && CanCast(spell, onUnit(a));
                    },
                    new Sequence(
                        new Action(a => write("{1}[SpellLocation] {0} ", spell, context)),
                        new Action(a => SpellManager.Cast(spell)),
                        new Action(a => SpellManager.ClickRemoteLocation(onUnit(a).Location))
                    )
                )
            );
        }


        public static Composite UseCooldown_Hunter_RapidFire()
        {
            return new Decorator(ret => CalculateTimeToDeath(Me.CurrentTarget) > 3 && SpellManager.Spells["Rapid Fire"].CooldownTimeLeft.Milliseconds < 100 && !XSCommon.IsMyAuraActive("Rapid Fire") && !XSCommon.IsMyAuraActive("Bloodlust") && !XSCommon.IsMyAuraActive("Heroism") && !XSCommon.IsMyAuraActive("Ancient Hysteria") && !XSCommon.IsMyAuraActive("Time Warp"),
                        new Action(delegate
                        {
                            if(XSCommon.CanCast("Rapid Fire"))
                                SpellManager.Cast("Rapid Fire");

                            //Logging.Write(Colors.Lime, "[CD] Rapid Fire");
                            return RunStatus.Failure;
                        }
                        )
                    );
        }

        public static Composite UseCooldown_Racial()
        {
            return new PrioritySelector(
                        new Decorator(ret => Me.Race == WoWRace.Troll && !SpellManager.Spells["Berserking"].Cooldown && CalculateTimeToDeath(Me.CurrentTarget) > 3,
                            new Action(delegate
                            {
                                Lua.DoString("RunMacroText('/Cast Berserking');");
                                Logging.Write(Colors.Lime, "[Racial] HULK SMASH (Berserking)");
                                return RunStatus.Failure;
                            }
                            )
                        ),
                        new Decorator(ret => Me.Race == WoWRace.Orc && !SpellManager.Spells["Blood Fury"].Cooldown && CalculateTimeToDeath(Me.CurrentTarget) > 3,
                            new Action(delegate
                            {
                                Lua.DoString("RunMacroText('/Cast Blood Fury');");
                                Logging.Write(Colors.Lime, "[Racial] HULK SMASH (Blood Fury)");
                                return RunStatus.Failure;
                            }
                            )
                        )
                    );
        }
        public static Composite UseCooldown_Trinket(int number, bool var)
        {
            switch (number)
            {
                case 1:
                    return new Decorator(ret => var && Me.Inventory.Equipped.Trinket1 != null && Me.Inventory.Equipped.Trinket1.Cooldown <= 0 && Me.Inventory.Equipped.Trinket1.Usable,
                                new Action(
                                    delegate
                                    {
                                        Lua.DoString("RunMacroText('/use 13');");
                                        {
                                            Logging.Write(Colors.Aquamarine, "[Trinket] {0}", Me.Inventory.Equipped.Trinket1.Name);
                                        }
                                        return RunStatus.Failure;
                                    }
                                )
                            );
                case 2:
                    return new Decorator(ret => var && Me.Inventory.Equipped.Trinket2 != null && Me.Inventory.Equipped.Trinket2.Cooldown <= 0 && Me.Inventory.Equipped.Trinket2.Usable,
                                new Action(
                                    delegate
                                    {
                                        Lua.DoString("RunMacroText('/use 14');");
                                        {
                                            Logging.Write(Colors.Aquamarine, "[Trinket] {0}", Me.Inventory.Equipped.Trinket2.Name);
                                        }
                                        return RunStatus.Failure;
                                    }
                                )
                            );
                default:
                    return new Decorator(ret => false,
                                new Action(
                                    delegate
                                    {
                                        return RunStatus.Failure;
                                    }
                                )
                            );
            }
        }
        public static Composite UseCooldown_Herbalism()
        {
            return CastSelfSpell("Lifeblood", ret => CalculateTimeToDeath(Me.CurrentTarget) > 3);
        }
        /*public static Composite UseCooldownEngineering()
        {

        }*/ //to be done

        
        public static Composite UseBagItem(string name, CanRunDecoratorDelegate cond)
        {
            WoWItem item = null;
            return new Decorator(
                delegate(object a)
                {
                    if (!cond(a))
                        return false;
                    item = Me.BagItems.FirstOrDefault(x => x.Name == name && x.Usable && x.Cooldown <= 0);
                    return item != null;
                },
            new Sequence(
                new Action(a => write(" [BagItem] {0} ", name)),
                new Action(a => item.UseContainerItem())));
        }
    }
}
